package com.tymoshenko.seabattle.shared.model.domain;

import java.io.Serializable;
import java.util.*;

/**
 * @author Yakiv Tymoshenko
 * @since 23.01.13
 */
public class Board implements Serializable {

    public static enum Status {
        FREE, DECK, BORDER, MISSED, DAMAGED, DESTROYED
    }

    public static final int WIDTH = 10;
    public static final int HEIGHT = 10;

    public static List<Point> shipNeighbourFields(Ship ship) {
        List<Point> decks = ship.getDecks();
        Point firstDeck = ship.getFirstDeck();
        Point lastDeck = ship.getLastDeck();

        List<Point> neighbours = new ArrayList<Point>();
        if (ship.getDirection() == Ship.Direction.VERTICAL) {
            neighbours.add(upperLeftDiagonalNeighbour(firstDeck));
            neighbours.add(upperNeighbour(firstDeck));
            neighbours.add(upperRightDiagonalNeighbour(firstDeck));
            for (Point deck : decks) {
                neighbours.add(leftNeighbour(deck));
                neighbours.add(rightNeighbour(deck));
            }
            neighbours.add(lowerLeftDiagonalNeighbour(lastDeck));
            neighbours.add(lowerNeighbour(lastDeck));
            neighbours.add(lowerRightDiagonalNeighbour(lastDeck));
        } else {
            neighbours.add(upperLeftDiagonalNeighbour(firstDeck));
            neighbours.add(leftNeighbour(firstDeck));
            neighbours.add(lowerLeftDiagonalNeighbour(firstDeck));
            for (Point deck : decks) {
                neighbours.add(upperNeighbour(deck));
                neighbours.add(lowerNeighbour(deck));
            }
            neighbours.add(upperRightDiagonalNeighbour(lastDeck));
            neighbours.add(rightNeighbour(lastDeck));
            neighbours.add(lowerRightDiagonalNeighbour(lastDeck));
        }

        // exclude out-of-board neighbours
        List<Point> result = new ArrayList<Point>(neighbours.size());
        for (Point neighbour : neighbours) {
            if (neighbour != null) {
                result.add(neighbour);
            }
        }

        return result;
    }

    private static Point upperNeighbour(Point point) {
        return Point.valueOf(point.getRow() - 1, point.getCol());
    }

    private static Point lowerNeighbour(Point point) {
        return Point.valueOf(point.getRow() + 1, point.getCol());
    }

    private static Point leftNeighbour(Point point) {
        return Point.valueOf(point.getRow(), point.getCol() - 1);
    }

    private static Point rightNeighbour(Point point) {
        return Point.valueOf(point.getRow(), point.getCol() + 1);
    }

    private static Point upperLeftDiagonalNeighbour(Point point) {
        return Point.valueOf(point.getRow() - 1, point.getCol() - 1);
    }

    private static Point upperRightDiagonalNeighbour(Point point) {
        return Point.valueOf(point.getRow() - 1, point.getCol() + 1);
    }

    private static Point lowerLeftDiagonalNeighbour(Point point) {
        return Point.valueOf(point.getRow() + 1, point.getCol() - 1);
    }

    private static Point lowerRightDiagonalNeighbour(Point point) {
        return Point.valueOf(point.getRow() + 1, point.getCol() + 1);
    }

    /*
    Point's row and col are from [1..10] range (NOT [0..9] !!!). It is controlled by the Point.valueOf(int, int) method
     */
    private Map<Point, Status> fields;   // TODO what if change it to Map<Status, Set<Point>>

    public Board() {
        initMapWithStatus(Status.FREE);
    }

    public void setFieldStatus(Point point, Status status) {
        fields.put(point, status);
    }

    public Map<Point, Status> getFields() {
        return Collections.unmodifiableMap(fields);
    }

    private void initMapWithStatus(Status status) {
        fields = new HashMap<Point, Status>(WIDTH * HEIGHT);
        for (int row = 0; row < HEIGHT; row++) {
            for (int col = 0; col < WIDTH; col++) {
                fields.put(Point.valueOf(row + 1, col + 1), status);
            }
        }
    }
}
